#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* 波特率设定被定义在 <asm/termbits.h>, 这在 <termios.h> 被引入 */
#define BAUDRATE B38400
/* 定义正确的序列埠 */
#define MODEMDEVICE "/dev/ttyUSB0"
#define _POSIX_SOURCE 1 /* POSIX 系统兼容 */

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE;

main()
{
  int fd,c, res;
  struct termios oldtio,newtio;
  char buf[255];
/* 
  开启数据机装置以读取并写入而不以控制 tty 的模式
  因为我们不想程序在送出 CTRL-C 后就被杀掉.
*/
  fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
  if(fd <0) 
  {
      perror(MODEMDEVICE); 
      exit(-1);
  }

  tcgetattr(fd,&oldtio); /* 储存目前的序列埠设定 */
  bzero(&newtio, sizeof(newtio)); /* 清除结构体以放入新的序列埠设定值 */

/* 
  BAUDRATE: 设定 bps 的速度. 你也可以用 cfsetispeed 及 cfsetospeed 来设定.
  CRTSCTS : 输出资料的硬件流量控制 (只能在具完整线路的缆线下工作
            参考 Serial-HOWTO 第七节)
  CS8     : 8n1 (8 位元, 不做同位元检查,1 个终止位元)
  CLOCAL  : 本地连线, 不具数据机控制功能
  CREAD   : 致能接收字元
*/
  newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
  //newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR | ICRNL;

/*
  Raw 模式输出.
*/
  newtio.c_oflag = 0;

/*
  ICANON  : 致能标准输入, 使所有回应机能停用, 并不送出信号以叫用程序
*/
  newtio.c_lflag = ICANON;

/* 
  初始化所有的控制特性
  预设值可以在 /usr/include/termios.h 找到, 在注解中也有,
  但我们在这不需要看它们
*/
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-/ */
 newtio.c_cc[VERASE]   = 0;     /* del */
 newtio.c_cc[VKILL]    = 0;     /* @ */
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
 newtio.c_cc[VTIME]    = 0;     /* 不使用分割字元组的计时器 */
 newtio.c_cc[VMIN]     = 1;     /* 在读取到 1 个字元前先停止 */
 newtio.c_cc[VSWTC]    = 0;     /* '/0' */
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
 newtio.c_cc[VEOL]     = 0;     /* '/0' */
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
 newtio.c_cc[VEOL2]    = 0;     /* '/0' */

/* 
  现在清除数据机线并启动序列埠的设定
*/
 tcflush(fd, TCIFLUSH);
 tcsetattr(fd,TCSANOW,&newtio);

/*
  终端机设定完成, 现在处理输入信号
  在这个范例, 在一行的开始处输入 'z' 会退出此程序.
*/
 while (STOP==FALSE) 
 {                          /* 回圈会在我们发出终止的信号后跳出 */
                            /* 即使输入超过 255 个字元, 读取的程序段还是会一直等到行终结符出现才停止.
                               如果读到的字元组低于正确存在的字元组, 则所剩的字元会在下一次读取时取得.
                               res 用来存放真正读到的字元组个数 */
    res = read(fd,buf,255); 
    buf[res]=0;             /* 设定字串终止字元, 所以我们能用 printf */
    printf(":%s:%d/n", buf, res);
    if (buf[0]=='z') STOP=TRUE;
 }
 /* 回存旧的序列埠设定值 */
 tcsetattr(fd,TCSANOW,&oldtio);
}
